package com.test.gs.plugin.lombokgsplugin;

import com.google.common.base.Predicates;
import com.google.common.collect.Collections2;
import com.google.common.collect.Lists;
import com.intellij.lang.java.JavaLanguage;
import com.intellij.psi.*;
import com.intellij.psi.augment.PsiAugmentProvider;
import com.intellij.psi.impl.light.LightMethodBuilder;
import com.intellij.psi.impl.light.LightParameter;
import com.intellij.psi.impl.source.PsiExtensibleClass;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.jetbrains.annotations.NotNull;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.stream.Stream;

/**
 * 欺骗idea的事情
 * 让idea认为这个类有set get方法
 */
public class LombokGsPsiAugmentProvider extends PsiAugmentProvider {


    public String getMethodUniqueKey(PsiMethod psiMethod) {
        String name = psiMethod.getName();
        PsiModifierList modifierList = psiMethod.getModifierList();
        String text = modifierList.getText();
        PsiType returnType = psiMethod.getReturnType();
        PsiParameterList parameterList = psiMethod.getParameterList();
        StringBuffer sb = new StringBuffer();
        if (parameterList.getParametersCount() == 0) {
            sb.append(name).append("()").append(returnType == null ? "null" : returnType.getCanonicalText());
        } else {
            sb.append(name).append("(");
            for (PsiParameter parameter : parameterList.getParameters()) {
                parameter.getType();
                sb.append(parameter.getType().getCanonicalText()).append(",");
            }
            sb.deleteCharAt(sb.length() - 1);
            sb.append(")").append(returnType.getCanonicalText());
        }
        return sb.toString();
    }


    @Override
    protected @NotNull <Psi extends PsiElement> List<Psi> getAugments(@NotNull PsiElement element, @NotNull Class<Psi> type) {
        if (element instanceof PsiClass) {
            PsiClass psiClass = (PsiClass) element;
            if (psiClass.isAnnotationType() || psiClass.isInterface()) {
                return Collections.emptyList();
            }
            // 获取注解
            PsiAnnotation psiAnnotation = psiClass.getAnnotation("com.test.anno.GSet");
            if (psiAnnotation != null) {
                // 注解存在
                // element 就是 PsiClass
                // type 就是 PsiMethod 即这个类的方法
                // type==PsiMethod 或者 type implements PsiMethod
                if (PsiMethod.class.isAssignableFrom(type)) {
                    System.out.println("nameHint:" + 1);
                    // 获取这个类的所有字段
                    List<PsiField> fields = Stream.of(psiClass.getFields()).filter(field ->
                            {
                                boolean isStatic = field.hasModifierProperty(PsiModifier.STATIC);
                                boolean isPrivate = field.hasModifierProperty(PsiModifier.PRIVATE);
                                boolean isProtected = field.hasModifierProperty(PsiModifier.PROTECTED);
                                return !isStatic  && (isPrivate || isProtected);
                            }
                    ).toList();


                    if (!CollectionUtils.isEmpty(fields)) {
                        List<PsiElement> psiElementList = new ArrayList<>();
                        List<String> methodUniqueKeys = collectClassMethodsIntern(psiClass).stream().map(this::getMethodUniqueKey).toList();
                        for (PsiField field : fields) {
                            String fieldName = field.getName();
                            String getMethodName = "get" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
                            String setMethodName = "set" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
                            PsiMethod getMethod = createCacheMethod(psiClass, getMethodName, null, field.getType(), field);
                            PsiMethod setMethod = createCacheMethod(psiClass, setMethodName, List.of(new LightParameter(field.getName(), field.getType(), psiClass))
                                    , field.getType(), field);


                            String getMethodUniqueKey = getMethodUniqueKey(getMethod);
                            String setMethodUniqueKey = getMethodUniqueKey(setMethod);
                            boolean hasGetMethod = methodUniqueKeys.contains(getMethodUniqueKey);
                            boolean hasSetMethod = methodUniqueKeys.contains(setMethodUniqueKey);
                            if (!hasGetMethod) {
                                psiElementList.add(getMethod);
                            }
                            if (!hasSetMethod) {
                                psiElementList.add(setMethod);
                            }

                        }
                        if (!psiElementList.isEmpty()) {
                            return (List<Psi>) psiElementList;
                        }
                    }
                }
            }
        }
        return Collections.emptyList();

    }


    /**
     *Class<String> stringClass = String.class;
     * Class<Object> objectClass = Object.class;
     *
     * // 因为String是Object的子类，所以返回true
     * boolean result1 = objectClass.isAssignableFrom(stringClass); // true
     *
     * // 因为String不是Integer的子类，所以返回false
     * Class<Integer> integerClass = Integer.class;
     * boolean result2 = integerClass.isAssignableFrom(stringClass); // false
     *
     * // 同一个类也是可分配的
     * boolean result3 = stringClass.isAssignableFrom(stringClass); // true
     */

    public static Collection<PsiMethod> collectClassMethodsIntern(@NotNull PsiClass psiClass) {
        if (psiClass instanceof PsiExtensibleClass) {
            List<PsiMethod> allMethods = new ArrayList<>();
            List<PsiMethod> ownMethods = ((PsiExtensibleClass) psiClass).getOwnMethods();
            if (psiClass.getSuperClass() != null && !Object.class.getCanonicalName().equals(psiClass.getSuperClass().getQualifiedName())) {
                Collection<PsiMethod> methods = collectClassMethodsIntern(psiClass.getSuperClass());
                if (CollectionUtils.isNotEmpty(methods)) {
                    allMethods.addAll(methods);
                }
            }
            if (CollectionUtils.isNotEmpty(ownMethods)) {
                allMethods.addAll(ownMethods);
            }
            return allMethods;
        } else {
            return Collections2.transform(
                    Collections2.filter(Lists.newArrayList(psiClass.getChildren()), Predicates.instanceOf(PsiMethod.class)),
                    PsiClassUtil.PSI_ELEMENT_TO_METHOD_FUNCTION);
        }
    }

    public static PsiMethod createCacheMethod(PsiClass psiClass, String methodName, List<PsiParameter> parameterList, PsiType returnType, PsiField field) {
        PsiManager psiManager = psiClass.getContainingFile().getManager();

        LightMethodBuilder lightMethodBuilder = new LightMethodBuilder(psiManager, JavaLanguage.INSTANCE, methodName);
        lightMethodBuilder.addModifier(PsiModifier.PUBLIC);
        lightMethodBuilder.setContainingClass(psiClass);
        if (ObjectUtils.isNotEmpty(parameterList)) {
            for (PsiParameter psiParameter : parameterList) {
                lightMethodBuilder.addParameter(psiParameter.getName(), psiParameter.getType());
            }
        }
        lightMethodBuilder.setMethodReturnType(returnType);
        lightMethodBuilder.setNavigationElement(field);
        return lightMethodBuilder;
    }
}
